﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using NLog;
using Infrastructure.Extensions;
using Infrastructure.Model.system;
using Infrastructure.GlobalException;


namespace Infrastructure.Attribute
{
    // <summary>
    /// 操作日志记录过滤器，继承自ActionFilterAttribute，用于在MVC动作执行完成后记录操作日志
    /// </summary>
    public class LogActionFilter : ActionFilterAttribute
    {
        // 使用NLog日志记录器
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 在动作结果执行完成后触发，记录操作日志
        /// </summary>
        public override void OnResultExecuted(ResultExecutedContext context)
        {
            // 检查并获取控制器动作描述符
            if (!(context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor))
            {
                return;
            }

            // 获取方法上的LogAttribute特性
            LogAttribute logAttribute = GetLogAttribute(controllerActionDescriptor);
            if (logAttribute == null)
            {
                return; // 如果没有标记LogAttribute则不记录
            }

            try
            {
                // 获取请求基本信息
                string httpMethod = context.HttpContext.Request.Method.ToUpper();
                string userName = context.HttpContext.GetName(); // 扩展方法获取用户名

                // 处理响应内容（仅处理JSON类型的ContentResult）
                string responseContent = string.Empty;
                if (context.Result is ContentResult { ContentType: "application/json" } contentResult)
                {
                    responseContent = contentResult.Content.Replace("\r\n", "").Trim();
                }

                // 获取控制器和动作名称
                string controllerName = context.RouteData.Values["Controller"].ToString();
                string actionName = context.RouteData.Values["Action"].ToString();

                // 创建操作日志实体
                SysOperLog operLog = new SysOperLog
                {
                    status = 0, // 默认状态为成功
                    operName = userName,
                    operIp = context.HttpContext.GetClientUserIp(), // 扩展方法获取客户端IP
                    operUrl = context.HttpContext.GetRequestUrl(),    // 扩展方法获取完整请求URL
                    requestMethod = httpMethod,
                    jsonResult = responseContent,
                    operLocation = "UNKNOWN", // 需要后续扩展地理位置解析功能
                    method = $"{controllerName}.{actionName}()", // 方法全称
                    operTime = DateTime.Now
                };

                // 获取请求参数（根据请求方法类型）
                GetRequestValue(operLog, context.HttpContext);

                // 如果存在LogAttribute，补充日志信息
                if (logAttribute != null)
                {
                    operLog.title = logAttribute.Title;
                    operLog.businessType = (int)(logAttribute?.BusinessType).Value;
                    operLog.operParam = logAttribute.IsSaveRequestData ? operLog.operParam : ""; // 根据配置决定是否保存请求参数
                    operLog.jsonResult = logAttribute.IsSaveResponseData ? operLog.jsonResult : ""; // 根据配置决定是否保存响应结果
                }
                // 通过DI容器获取日志服务并插入日志
                ISysOperLogService logService = (ISysOperLogService)App.GetRequiredService(typeof(ISysOperLogService));
                logService.InsertOperlog(operLog);

                // 记录NLog日志（结构化日志）
                LogEventInfo logEvent = new LogEventInfo(LogLevel.Info, "GlobalExceptionMiddleware", "");
                logEvent.Properties["status"] = 0;
                logEvent.Properties["jsonResult"] = HttpMethods.IsGet(httpMethod) ? "" : responseContent; // GET请求不记录响应体
                logEvent.Properties["requestParam"] = operLog.operParam;
                logEvent.Properties["user"] = context.HttpContext.User.Identity.Name;
                logger.Log(logEvent);
            }
            catch (Exception ex)
            {
                // 日志记录本身出错时记录错误，避免影响主流程
                logger.Error(ex, $"记录操作日志出错了#{ex.Message}");
            }
        }

        /// <summary>
        /// 获取控制器方法上的LogAttribute特性
        /// </summary>
        private LogAttribute GetLogAttribute(ControllerActionDescriptor descriptor)
        {
            return (LogAttribute)descriptor.MethodInfo
                .GetCustomAttributes(inherit: true)
                .FirstOrDefault(a => a.GetType() == typeof(LogAttribute));
        }

        /// <summary>
        /// 获取请求参数（根据请求方法类型处理）
        /// </summary>
        public static void GetRequestValue(SysOperLog operLog, HttpContext context)
        {
            string method = operLog.requestMethod;
            string param;

            if (HttpMethods.IsPost(method) || HttpMethods.IsPut(method))
            {
                // 重置流位置以便读取（需要确保请求体已被缓冲）
                context.Request.Body.Seek(0L, SeekOrigin.Begin);
                using (StreamReader reader = new StreamReader(context.Request.Body, Encoding.UTF8))
                {
                    param = reader.ReadToEndAsync().Result;
                }
            }
            else
            {
                param = context.Request.QueryString.Value.ToString();
            }

            operLog.operParam = param;
        }
    }
}
